/******************************************************************************* * Copyright (c) 2004, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal.layout; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; /** * <code>CellData</code> is the layout data object associated with * <code>CellLayout</code>. You can attach a CellData object to a * control by using the <code>setLayoutData</code> method. CellData * objects are optional. If you do not attach any layout data to a control, * it will behave just like attaching a CellData created using its default * constructor. * * @since 3.0 **/ public final class CellData { /** * hintType flag (value = 0) indicating that the control's computeSize method should be used * to determine the control size. If modifierType is set to NONE, then the widthHint * and heightHint fields will be ignored. */ public final static int NONE = 0; /** * hintType flag (value = 1) indicating that the widthHint and heightHint should be used * as the control's size instead of the result of computeSize * <p> * This flag is useful for list boxes, text boxes, tree controls, and other controls * whose contents can change dynamically. For example, create a tree control and set * its width and height hints to the default size for that control. This will cause * the hints to be used instead of the preferred size of the tree control.</p> */ public final static int OVERRIDE = 1; /** * hintType(value = 2) indicating that the width of the control should be no less than * widthHint (if provided) and the height of the control should be no less * than heightHint (if provided). * <p> * This flag is useful for buttons. For example, set the width and height hints to * the default button size. This will use the default button size unless the button * label is too large to fit on the button. * </p> */ public final static int MINIMUM = 2; /** * hintType flag (value = 3) indicating that the width of the control should be no more than * widthHint (if provided) and the height of the control should be no more * than heightHint (if provided). * <p> * This flag is useful for wrapping text. For example, set heightHint to SWT.DEFAULT * and set widthHint to the desired number of pixels after which text should wrap. This * will cause the text to wrap after the given number of pixels, but will not allocate * extra space in the column if the text widget does not fill an entire line. * </p> */ public final static int MAXIMUM = 3; /** * This flag controls how the width and height hints are to be treated. See the constants * above. */ public int hintType = OVERRIDE; /** * Width hint. This modifies the width of the control, in pixels. If set to SWT.DEFAULT, * this dimension will not be constrained. Depending on the value of modifierType, * this may be a minimum size, a maximum size, or simply replace the preferred control * size. */ public int widthHint = SWT.DEFAULT; /** * Height hint. This modifies the height of the control, in pixels. If set to SWT.DEFAULT, * this dimension will not be constrained. Depending on the value of modifierType, * this will be a minimum size, a maximum size, or a replacement for the control's preferred * size. */ public int heightHint = SWT.DEFAULT; /** * Number of rows spanned by this cell (default = 1) */ public int verticalSpan = 1; /** * Number of columns spanned by this cell (default = 1) */ public int horizontalSpan = 1; /** * Horizontal alignment of the control within the cell. May be one * of SWT.LEFT, SWT.RIGHT, SWT.CENTER, or SWT.NORMAL. SWT.NORMAL indicates * that the control should be made as wide as the cell. */ public int horizontalAlignment = SWT.FILL; /** * Vertical alignment of the control within the cell. May be one of * SWT.TOP, SWT.BOTTOM, SWT.CENTER, or SWT.NORMAL. SWT.NORMAL indicates * that the control should be made as wide as the cell. */ public int verticalAlignment = SWT.FILL; /** * Horizontal indentation (pixels). Positive values move the control * to the right, negative to the left. */ public int horizontalIndent = 0; /** * Vertical indentation (pixels). Positive values move the control * down, negative values move the control up. */ public int verticalIndent = 0; /** * Constructs a CellData with default properties */ public CellData() { // Use the default values for all fields. } /** * Creates a new CellData that with properties that are as close as possible to * the given GridData. This is used for converting GridLayouts into CellLayouts. * * @param data */ public CellData(GridData data) { verticalSpan = data.verticalSpan; horizontalSpan = data.horizontalSpan; switch (data.horizontalAlignment) { case GridData.BEGINNING: horizontalAlignment = SWT.LEFT; break; case GridData.CENTER: horizontalAlignment = SWT.CENTER; break; case GridData.END: horizontalAlignment = SWT.RIGHT; break; case GridData.FILL: horizontalAlignment = SWT.FILL; break; } switch (data.verticalAlignment) { case GridData.BEGINNING: verticalAlignment = SWT.LEFT; break; case GridData.CENTER: verticalAlignment = SWT.CENTER; break; case GridData.END: verticalAlignment = SWT.RIGHT; break; case GridData.FILL: verticalAlignment = SWT.FILL; break; } widthHint = data.widthHint; heightHint = data.heightHint; horizontalIndent = data.horizontalIndent; hintType = OVERRIDE; } /** * Copies the given CellData * * @param newData */ public CellData(CellData newData) { hintType = newData.hintType; widthHint = newData.widthHint; heightHint = newData.heightHint; horizontalAlignment = newData.horizontalAlignment; verticalAlignment = newData.verticalAlignment; horizontalSpan = newData.horizontalSpan; verticalSpan = newData.verticalSpan; } /** * Sets the size hint for this control. This is used to modify the control's * preferred size. If one dimension should remain unmodified, that hint can be * set to SWT.DEFAULT. Using a size hint of CellData.MINIMUM ensures that the preferred * control size is larger than the hint. Using a size hint of CellData.MAXIMUM ensures * that the preferred size is smaller than the hint. Using a size hint of CellData.OVERRIDE * ensures that the preferred size is always equal to the hint. * * @param hintType one of CellData.MINIMUM, CellData.MAXIMUM, or CellData.OVERRIDE * @param hint size hint (in pixels). If either dimension is set to SWT.DEFAULT, the * hint will not affect that dimension * @return this */ public CellData setHint(int hintType, Point hint) { return setHint(hintType, hint.x, hint.y); } /** * Sets the size hint for this control. This is used to modify the control's * preferred size. If one dimension should remain unmodified, that hint can be * set to SWT.DEFAULT. Using a size hint of CellData.MINIMUM ensures that the preferred * control size is larger than the hint. Using a size hint of CellData.MAXIMUM ensures * that the preferred size is smaller than the hint. Using a size hint of CellData.OVERRIDE * ensures that the preferred size is always equal to the hint. If both hints are equal * to SWT.DEFAULT, then the control's preferred size is unmodified. * * @param hintType one of CellData.MINIMUM, CellData.MAXIMUM, or CellData.OVERRIDE * @param horizontal horizontal hint (pixels). A value of SWT.DEFAULT will leave the result * of the control's computeSize method unmodified. * @param vertical vertical hint (pixels). A value of SWT.DEFAULT will leave the result of * the control's computeSize method unmodified. * @return this */ public CellData setHint(int hintType, int horizontal, int vertical) { this.hintType = hintType; this.heightHint = vertical; this.widthHint = horizontal; return this; } /** * Sets the alignment for this control * * @param horizontalAlignment one of SWT.LEFT, SWT.RIGHT, SWT.FILL, or SWT.CENTER * @param verticalAlignment one of SWT.TOP, SWT.BOTTOM, SWT.FILL, or SWT.CENTER * @return this */ public CellData align(int horizontalAlignment, int verticalAlignment) { this.horizontalAlignment = horizontalAlignment; this.verticalAlignment = verticalAlignment; return this; } /** * Sets the number of rows and columns spanned by this control. * * @param horizontalSpan number of columns spanned by the control (> 0) * @param verticalSpan number of rows spanned by the control (> 0) * @return this */ public CellData span(int horizontalSpan, int verticalSpan) { this.horizontalSpan = horizontalSpan; this.verticalSpan = verticalSpan; return this; } /** * Sets the indentation for this control. The indentation is added to * the control's position within the cell. For example, indentation of * (10,4) will move the control right by 10 pixels and down by 4 pixels. * * @param indent indentation (pixels) * @return this */ public CellData indent(Point indent) { return this.indent(indent.x, indent.y); } /** * Sets the indentation for this cell * * @param horizontalIndent distance (pixels) to move the control to the right * @param verticalIndent distance (pixels) to move the control down * @return this */ public CellData indent(int horizontalIndent, int verticalIndent) { this.horizontalIndent = horizontalIndent; this.verticalIndent = verticalIndent; return this; } /** * Returns the preferred size of the given control, given the known dimensions of * its cell. * * @param toCompute the control whose size is to be computed * @param cellWidth width of the cell, in pixels (or SWT.DEFAULT if unknown) * @param cellHeight height of the cell, in pixels (or SWT.DEFAULT if unknown) * @return the preferred size of the given control, in pixels */ public Point computeSize(SizeCache toCompute, int cellWidth, int cellHeight) { int absHorizontalIndent = Math.abs(horizontalIndent); int absVerticalIndent = Math.abs(verticalIndent); // If we're going to indent, subtract off the space that will be required for indentation from // the available space if (cellWidth != SWT.DEFAULT) { cellWidth -= absHorizontalIndent; } if (cellHeight != SWT.DEFAULT) { cellHeight -= absVerticalIndent; } int controlWidth = horizontalAlignment == SWT.FILL ? cellWidth : SWT.DEFAULT; int controlHeight = verticalAlignment == SWT.FILL ? cellHeight : SWT.DEFAULT; // Note: this could be optimized further. If we're using a MAXIMUM hint and // non-FILL alignment, we could simply call computeMaximumBoundedSize using the // minimum of the cell size and the hint as the boundary -- basically, rather // than applying two limits for the hint and the cell boundary, we can do it in // one step and reduce the size computations by half (for this specific case). Point controlSize = computeControlSize(toCompute, controlWidth, controlHeight); if (cellWidth != SWT.DEFAULT && controlSize.x > cellWidth) { controlSize = computeControlSize(toCompute, cellWidth, controlHeight); if (cellHeight != SWT.DEFAULT && controlSize.y > cellHeight) { controlSize.y = cellHeight; } } else if (cellHeight != SWT.DEFAULT && controlSize.y > cellHeight) { controlSize = computeControlSize(toCompute, controlWidth, cellHeight); if (cellWidth != SWT.DEFAULT && controlSize.x > cellWidth) { controlSize.x = cellWidth; } } // If we're going to indent, add the indentation to the required space controlSize.x += absHorizontalIndent; controlSize.y += absVerticalIndent; return controlSize; } /** * Arranges the given control within the given rectangle using the * criteria described by this CellData. * * @param control * @param cellBounds * @since 3.0 */ public void positionControl(SizeCache cache, Rectangle cellBounds) { int startx = cellBounds.x; int starty = cellBounds.y; int availableWidth = cellBounds.width - horizontalIndent; int availableHeight = cellBounds.height - verticalIndent; Point size = computeSize(cache, availableWidth, availableHeight); // Horizontal justification switch (horizontalAlignment) { case SWT.RIGHT: startx = cellBounds.x + availableWidth - size.x; break; case SWT.CENTER: startx = cellBounds.x + (availableWidth - size.x) / 2; break; } // Vertical justification switch (verticalAlignment) { case SWT.BOTTOM: starty = cellBounds.y + availableHeight - size.y; break; case SWT.CENTER: starty = cellBounds.y + (availableHeight - size.y) / 2; break; } // Position the control cache.getControl().setBounds(startx + horizontalIndent, starty + verticalIndent, size.x, size.y); } /** * Returns the preferred size of the given control in this cell, given one or both * known dimensions of the control. This differs from computeSize, which takes known * dimensions of the <b>cell</b> as arguments. * * @param toCompute * @param controlWidth * @param controlHeight * @return * @since 3.0 */ private Point computeControlSize(SizeCache toCompute, int controlWidth, int controlHeight) { switch (hintType) { case OVERRIDE: return computeOverrideSize(toCompute, controlWidth, controlHeight, widthHint, heightHint); case MINIMUM: return computeMinimumBoundedSize(toCompute, controlWidth, controlHeight, widthHint, heightHint); case MAXIMUM: return computeMaximumBoundedSize(toCompute, controlWidth, controlHeight, widthHint, heightHint); } return computeRawSize(toCompute, controlWidth, controlHeight); } /** * Computes the size of the control, given its outer dimensions. This should be used in * place of calling Control.computeSize, since Control.computeSize takes control-specific * inner dimensions as hints. * * @param toCompute Control whose size will be computed * @param controlWidth width of the control (pixels or SWT.DEFAULT if unknown) * @param controlHeight height of the control (pixels or SWT.DEFAULT if unknown) * @return preferred dimensions of the control */ private static Point computeRawSize(SizeCache toCompute, int controlWidth, int controlHeight) { if (controlWidth != SWT.DEFAULT && controlHeight != SWT.DEFAULT) { return new Point(controlWidth, controlHeight); } // Known bug: we pass the OUTER dimension of the control into computeSize, even though // SWT expects a control-specific inner dimension as width and height hints. Currently, // SWT does not provide any means to convert outer dimensions into inner dimensions. // Fortunately, the outer and inner dimensions tend to be quite close so we // pass in the outer dimension and adjust the result if it differs from one of the // hints. This may cause incorrect text wrapping in rare cases, and should be fixed // once SWT provides a way to convert the outer dimension of a control into a valid // width or height hint for Control.computeSize. Note that the distinction between outer // and inner dimensions is undocumented in SWT, and most examples also contain this // bug. Point result = toCompute.computeSize(controlWidth, controlHeight); // Hack: If the result of computeSize differs from the width or height-hints, adjust it. // See above. Don't remove this hack until SWT provides some way to pass correct width // and height hints into computeSize. Once this happens, these conditions should always // return false and the hack will have no effect. if (controlWidth != SWT.DEFAULT) { result.x = controlWidth; } else if (controlHeight != SWT.DEFAULT) { result.y = controlHeight; } return result; } /** * Computes the preferred size of the control. Optionally, one or both dimensions * may be fixed to a given size. * * @param control object that can compute the size of the control of interest * @param wHint known width (or SWT.DEFAULT if the width needs to be computed) * @param hHint known height (or SWT.DEFAULT if the height needs to be computed) * @param overrideW width that should always be returned by the control, * or SWT.DEFAULT if the width is not being constrained * @param overrideH height that should always be returned by the control, * or SWT.DEFAULT if the height is not being constrained * @return */ private static Point computeOverrideSize(SizeCache control, int wHint, int hHint, int overrideW, int overrideH) { int resultWidth = overrideW; int resultHeight = overrideH; if (wHint != SWT.DEFAULT) { resultWidth = wHint; } if (hHint != SWT.DEFAULT) { resultHeight = hHint; } if (resultWidth == SWT.DEFAULT || resultHeight == SWT.DEFAULT) { Point result = computeRawSize(control, resultWidth, resultHeight); return result; } return new Point(resultWidth, resultHeight); } /** * Computes the size for the control, optionally bounding the size in the x and * y directions. The various hints are used to determine which dimensions are * already known and which dimensions need to be computed. * * @param control The control whose size should be computed * @param wHint known width (or SWT.DEFAULT if the width needs to be computed) * @param hHint known height (or SWT.DEFAULT if the height needs to be computed) * @param boundedWidth maximum width for the control (or SWT.DEFAULT if the width is unbounded) * @param boundedHeight maximum height for the control (or SWT.DEFAULT if the height is unbounded) * @return the preferred size of the control, given that it cannot exceed the given bounds */ private static Point computeMaximumBoundedSize(SizeCache control, int wHint, int hHint, int boundedWidth, int boundedHeight) { Point controlSize = computeRawSize(control, wHint, hHint); if (wHint == SWT.DEFAULT && boundedWidth != SWT.DEFAULT && controlSize.x > boundedWidth) { return computeMaximumBoundedSize(control, boundedWidth, hHint, boundedWidth, boundedHeight); } if (hHint == SWT.DEFAULT && boundedHeight != SWT.DEFAULT && controlSize.y > boundedHeight) { return computeMaximumBoundedSize(control, wHint, boundedHeight, boundedWidth, boundedHeight); } return controlSize; } private static Point computeMinimumBoundedSize(SizeCache control, int wHint, int hHint, int minimumWidth, int minimumHeight) { Point controlSize = computeRawSize(control, wHint, hHint); if (minimumWidth != SWT.DEFAULT && wHint == SWT.DEFAULT && controlSize.x < minimumWidth) { return computeMinimumBoundedSize(control, minimumWidth, hHint, minimumWidth, minimumHeight); } if (minimumHeight != SWT.DEFAULT && hHint == SWT.DEFAULT && controlSize.y < minimumHeight) { return computeMinimumBoundedSize(control, wHint, minimumHeight, minimumWidth, minimumHeight); } return controlSize; } }